package ambiente;

import interfaces.IMetodosJason;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import controle.dados.Constants;
import controle.dados.Constants.MeuLadoCampo;
import robocode.CalculosRoboCode;
import robocode.ModelSalvarRefem;
import robocode.DadosRobos.DadosRobos;
import robocode.DadosRobos.RoboScaneado;
import tcp.TCPClient;
import tcp.TCPServer;
import tcp.interfaces.IJason;
import jason.asSyntax.Literal;
import jason.asSyntax.Structure;
import jason.environment.Environment;

public class EnvSalvarRefem extends Environment implements IJason, IMetodosJason {
	private CalculosRoboCode _cr = new CalculosRoboCode();
	private final int _numeroRobos = 5;  //representa o numero de robos que existem no ambiente robocode
	private final int _numeroAgentes = 2;
	private long[] _ultimaExecucao;
	private boolean _podeDormir = false;
	private long _tempoRequisicaoCadaRobo = 200L;
	List<RoboScaneado> _robos = new ArrayList<RoboScaneado>();
	private ControlePercepcoes _ctrlPercepcoes = new ControlePercepcoes(this, _numeroRobos, _cr);
	
	
	public MeuLadoCampo getMLC() { return Constants.mlc;}
	
	//por enquanto...
	private String _nomeRoboRefem = "sample.RoboRefem";
	private String _nomeRoboPrender = "sample.RoboPrisioneiro";
	//private String _nomeRoboInimigo = "sample.RoboInimigo";
	
	private List<Integer> _portaRobos;
	private String _ip = "localhost";
	//private int _numeroRound = 0;

	private boolean _esperar = false;
	private TCPServer _server = null;
	private int _portaServidorTCPJason = 7770;
	
//	public final String agSave = "agRobotSaver";
	public final String agRefem = "agRefem";
	//public final String agInimigo = "agInimigo";
	public final String agQuatroAcoes = "agQuatroAcoes";
	
	public int getWCampo() { return Constants.WIDTH; }
	public int getHCampo() { return Constants.HEIGHT; }
	
    //private static Logger logger = Logger.getLogger(EnvSalvarRefem.class.getName());
    private ModelSalvarRefem model;
    
    //----------------------------------------------------
    public void addRobos(DadosRobos dados) {
		String nomeRobo = "sample.RoboFazNada (" + dados.getIndiceRobo() + ")";
		RoboScaneado r = new RoboScaneado(nomeRobo, dados.getHeading(), dados.getX(), dados.getY(), false);
		addRobos(r);
	}
	public void addRobos(RoboScaneado r) {
		if (getIndiceNomeRobo(r.getNomeRobo())==0) return; 
		int indice = getIndice(r);
		if (indice < 0) {
			_robos.add(r);
		} else { //update
			_robos.set(indice, r);
		}
	}
	public int getIndiceNomeRobo(String nomeRobo) {
		if (nomeRobo.equals(_nomeRoboRefem)) {
			return 5;
		} //else if (nomeRobo.equals(_nomeRoboInimigo)){
//			return 7;
//		}
		if (nomeRobo.lastIndexOf('(') > 0) return Integer.valueOf(nomeRobo.substring(nomeRobo.lastIndexOf('(') + 1, nomeRobo.lastIndexOf(')')));
		return -1;
	}
	private int getIndice(RoboScaneado r){
		if (_robos.size() <= 0) return -1;
		for (int i = 0; i < _robos.size(); i++) {
			if (_robos.get(i).equals(r)) { return i; }
		}
		return -1;
	}
	
	public int getIndiceLista(String nomeRobo){
		if (_robos.size() <= 0) return -1;
		for (int i = 0; i < _robos.size(); i++) {
			if (_robos.get(i).getNomeRobo().equals(nomeRobo)) { return i; }
		}
		return -1;
	}
	
	public RoboScaneado getRobo(int indice) {
		if (indice < 0) return null;
		if (indice < 0) { //tto especial (por enquanto) - inimigo
//			System.err.println("ERRRO - " + indice);
//			aguardar(1000L);
//			System.exit(0);
			
		}
		if (_robos.size() <= 0 || _robos.size() < indice) return null;
		return _robos.get(indice);
	}
	public RoboScaneado getRoboRefemSalvar() {
		return getRobo(getIndiceLista(_nomeRoboRefem));
	}
	public RoboScaneado getRoboRefemPrender() {
		return getRobo(getIndiceLista(_nomeRoboPrender));
	}
	public List<RoboScaneado> getRobosInimigos() {
		List<RoboScaneado> rt = new ArrayList<RoboScaneado>();
		if (_robos==null || _robos.size() <= 0) return rt;
		for (RoboScaneado r : _robos) {
			if (getIndiceNomeRobo(r.getNomeRobo()) < 0 && 
					!r.getNomeRobo().equals(_nomeRoboRefem) && 
					!r.getNomeRobo().equals(_nomeRoboPrender)){
				rt.add(r);
			}
		}
		return rt;
	}
	/**
	 * Retorna o robo inimigo mais proximo do meu robo
	 * */
	public RoboScaneado getRoboInimigo(String nomeRobo) {
		List<RoboScaneado> robosInimigos = getRobosInimigos();
		if (robosInimigos==null||robosInimigos.size()<=0){return null;}
		int index = this.getIndiceLista(nomeRobo);
		RoboScaneado rb = this.getRobo(index);
		RoboScaneado rbInimigo = null;
		
		double dist1 = 0, dist2 = 0;
		for (RoboScaneado r : robosInimigos) {
			if (r==null) continue;
			if (rbInimigo==null){
				rbInimigo = r;
				dist1 = DistanciaRobos(rb, rbInimigo);
			} else {
				dist2 = DistanciaRobos(rb, rbInimigo);
				if (dist1 < dist2) {
					rbInimigo = r;
					dist1 = dist2;
				}
			}
		}
		return rbInimigo;
	}
	
	private double DistanciaRobos(RoboScaneado rb1, RoboScaneado rb2){
    	if (rb1==null||rb2==null) return -1.0;
    	double distancia = _cr.getDistanciaRobos(rb1, rb2);
    	return distancia;
    }
	//----------------------------------------------------
//	public String getAgSave() { return agSave; }
	public String getAgRefem() { return getAgQuatroAcoes() /*agRefem*/; }
	//public String getAgInimigo() { return agInimigo; }
	public String getAgQuatroAcoes() { return agQuatroAcoes; }
	//----------------------------------------------------

	public int getPorta(int indice) { return _portaRobos.get(indice); }
	public String getIp() { return _ip; }
	
    @Override public void init(String[] args) {
    	if (_podeDormir) iniciaServidorTCP();
    	_ultimaExecucao = new long[_numeroRobos];
		for (int i = 0; i < _ultimaExecucao.length; i++) {_ultimaExecucao[i]=0L;}
		
    	configuracoes();
        model = new ModelSalvarRefem(getWCampo(), getHCampo(), this, _numeroAgentes, _numeroRobos, _cr);
        updatePercepts();
    }
    
    /**
     * seta as configuracoes dos clientes tcp
     * */
    private void configuracoes(){
    	//4 robos jogadores, 1 robo refem, 1 robo scanner [6]
    	
    	_portaRobos = new ArrayList<Integer>();
    	_portaRobos.add(7891); //0
    	_portaRobos.add(7892); //1
    	_portaRobos.add(7893); //2
    	_portaRobos.add(7894); //3
    	_portaRobos.add(7900); //refem
    	
    	//_portaRobos.add(7895); //robo refem do inimigo (o meu refem)
    	//_portaRobos.add(7896); //inimigo --> por enquanto
    	
    	_ip = "localhost";
    	//recupera os dados dos robos via TCP
    	atualizarDadosRobosViaTCP();
    }
    
    private void atualizaDadosViaTCP(int indice) {
    	boolean eq0 = _ultimaExecucao[indice] == 0L;
    	if (eq0) {_ultimaExecucao[indice] = System.currentTimeMillis();}
    	long dif = System.currentTimeMillis() - _ultimaExecucao[indice];
    	if (dif >= _tempoRequisicaoCadaRobo || eq0) {
    		_ultimaExecucao[indice] = System.currentTimeMillis(); //update
    		
//	    	EnviarMsgTCP en = new EnviarMsgTCP(TipoEnvio.pedirDados, getPorta(indice), _ip, this, indice);
//	    	en.setNumeroRobo(indice);
//	    	runTask(en, true);
	    	//en.start();
	    	
    	    /**
    	     * Pede ao robo as informacoes relacionadas aos robos escaneados<br />
    	     * as informa��es prov�m do onScannedRobot
    	     * */

    		TCPClient cli = new TCPClient(getPorta(indice), _ip);
    		List<RoboScaneado> robos = cli.pedirDadosRobosEscaneados();
    		for (RoboScaneado r : robos) {
    			if (r==null) continue;
    			addRobos(r);
    		}
    	}
    }
   
	public void runTask(Runnable r, boolean separateThread) {
		if (separateThread) {
			(new Thread(r)).start();
		} else {
			r.run();
		}
	}
    
	private void aguardar(long tempo){ try { Thread.sleep(tempo); } catch (InterruptedException e) { e.printStackTrace(); } }

    //-------------------------------
    @Override public boolean executeAction(String ag, Structure action) {
        //logger.info(ag+" doing: "+ action);

        atualizarDadosRobosViaTCP();
        if (_podeDormir) {
			while (_esperar) { // espera, ate que o robo termine a sua execucao
				//System.out.println("### JASON ESTOU ESPERANDO....");
				aguardar(100);
			}
        }
        
        //action.getFunctor().equals
        //System.out.println("#action: " + action);
        
        try {
        	_ctrlPercepcoes.RealizarControlePercepcoes();
        	
        	RoboScaneado rb1 = getRobo(getIndiceLista("sample.RoboFazNada (1)"));
        	RoboScaneado rb2 = getRobo(getIndiceLista("sample.RoboFazNada (2)"));
        	RoboScaneado rb3 = getRobo(getIndiceLista("sample.RoboFazNada (3)"));
        	RoboScaneado rb4 = getRobo(getIndiceLista("sample.RoboFazNada (4)"));
        	RoboScaneado rRefemSalvar = getRoboRefemSalvar();
        	RoboScaneado rRefemPrender = getRoboRefemPrender();
        	
        	if (action.equals(Literal.parseLiteral("aproximar1")) ){
        		if (rb1!=null && !rb1.getExecutando())
        			model.AproximarRefem(rb1);
        	} else if (action.equals(Literal.parseLiteral("defend1"))) {
        		if (rb1!=null && rRefemPrender!=null && !rb1.getExecutando())
        			model.Defend(rb1, rRefemPrender);
            } else if (action.equals(Literal.parseLiteral("atack1"))) {
            	if (rb1!=null && !rb1.getExecutando()) {
            		RoboScaneado rInimigo = getRoboInimigo(rb1.getNomeRobo());
            		if (rInimigo != null)
            			model.Atack(rb1, rInimigo);
            	}
            } else if (
          			action.equals(Literal.parseLiteral("voltarma1"))
          			|| action.equals(Literal.parseLiteral("voltarminhaarea1"))
         			) {
            	if (rb1!=null && !rb1.getExecutando())
            		model.VoltarMinhaArea(rb1);

        	} else if (action.equals(Literal.parseLiteral("aproximar2")) ){
        		if (rb2!=null && !rb2.getExecutando())
        			model.AproximarRefem(rb2);
        	} else if (action.equals(Literal.parseLiteral("defend2"))) {
        		if (rb2!=null && rRefemPrender!=null && !rb2.getExecutando())
        			model.Defend(rb2, rRefemPrender);
            } else if (action.equals(Literal.parseLiteral("atack2"))) {
            	if (rb2!=null && !rb2.getExecutando()) {
            		RoboScaneado rInimigo = getRoboInimigo(rb2.getNomeRobo());
            		if (rInimigo != null)
            			model.Atack(rb2, rInimigo);
            	}
            } else if (
          			action.equals(Literal.parseLiteral("voltarma2"))
          			|| action.equals(Literal.parseLiteral("voltarminhaarea2"))
         			) {
            	if (rb2!=null && !rb2.getExecutando())
            		model.VoltarMinhaArea(rb2);

            	
            } else if (action.equals(Literal.parseLiteral("aproximar3")) ){
        		if (rb3!=null && !rb3.getExecutando())
        			model.AproximarRefem(rb3);
        	} else if (action.equals(Literal.parseLiteral("defend3"))) {
        		if (rb3!=null && rRefemPrender!=null && !rb3.getExecutando())
        			model.Defend(rb3, rRefemPrender);
            } else if (action.equals(Literal.parseLiteral("atack3"))) {
            	if (rb3!=null && !rb3.getExecutando()) {
            		RoboScaneado rInimigo = getRoboInimigo(rb3.getNomeRobo());
            		if (rInimigo != null)
            			model.Atack(rb3, rInimigo);
            	}
            } else if (
          			action.equals(Literal.parseLiteral("voltarma3"))
          			|| action.equals(Literal.parseLiteral("voltarminhaarea3"))
         			) {
            	if (rb3!=null && !rb3.getExecutando())
            		model.VoltarMinhaArea(rb3);
        		
        		
            } else if (action.equals(Literal.parseLiteral("aproximar4")) ){
        		if (rb4!=null && !rb4.getExecutando())
        			model.AproximarRefem(rb4);
        	} else if (action.equals(Literal.parseLiteral("defend4"))) {
        		if (rb4!=null && rRefemPrender!=null && !rb4.getExecutando())
        			model.Defend(rb4, rRefemPrender);
            } else if (action.equals(Literal.parseLiteral("atack4"))) {
            	if (rb4!=null && !rb4.getExecutando()) {
            		RoboScaneado rInimigo = getRoboInimigo(rb4.getNomeRobo());
            		if (rInimigo != null)
            			model.Atack(rb4, rInimigo);
            	}
            } else if (
          			action.equals(Literal.parseLiteral("voltarma4"))
          			|| action.equals(Literal.parseLiteral("voltarminhaarea4"))
         			) {
            	if (rb4!=null && !rb4.getExecutando())
            		model.VoltarMinhaArea(rb4);
            
            //----------------------------------------
            } else if (action.equals(Literal.parseLiteral("refemrbseguir1"))) {
        		if (rb1!=null && rRefemSalvar!=null && !rRefemSalvar.getExecutando())
        			model.SeguirRoboSalvador(rb1, rRefemSalvar);
        		System.out.println("Seguir rb 1");
        		
            } else if (action.equals(Literal.parseLiteral("refemrbseguir2"))) {
        		if (rb2!=null && rRefemSalvar!=null && !rRefemSalvar.getExecutando())
        			model.SeguirRoboSalvador(rb2, rRefemSalvar);
        		System.out.println("Seguir rb 2");
        		
            } else if (action.equals(Literal.parseLiteral("refemrbseguir3"))) {
        		if (rb3!=null && rRefemSalvar!=null && !rRefemSalvar.getExecutando())
        			model.SeguirRoboSalvador(rb3, rRefemSalvar);
        		System.out.println("Seguir rb 3");
        		
            } else if (action.equals(Literal.parseLiteral("refemrbseguir4"))) {
        		if (rb4!=null && rRefemSalvar!=null && !rRefemSalvar.getExecutando())
        			model.SeguirRoboSalvador(rb4, rRefemSalvar);
        		System.out.println("Seguir rb 4");
        		
            } else if (action.equals(Literal.parseLiteral("defend5"))) {
            	if (rRefemSalvar!=null && !rRefemSalvar.getExecutando())
            		model.Defend(rRefemSalvar, rRefemPrender);
            	
            } else if (action.equals(Literal.parseLiteral("atack5"))) {
            	if (rRefemSalvar!=null && !rRefemSalvar.getExecutando()) {
            		RoboScaneado rInimigo = getRoboInimigo(rb3.getNomeRobo());
            		if (rInimigo != null)
            			model.Atack(rRefemSalvar, rInimigo);
            	}
        		
//--------------------------------------
        		
//            } else if (action.equals(Literal.parseLiteral("bloquearSalvador1"))) { //robo inimigo - remover no futuro
//        		model.BloquearAction("sample.RoboFazNada (1)",_nomeRoboInimigo); //no modelo final nao sera tratado
//            } else if (action.equals(Literal.parseLiteral("bloquearSalvador2"))) { //robo inimigo - remover no futuro
//            	model.BloquearAction("sample.RoboFazNada (2)",_nomeRoboInimigo); //no modelo final nao sera tratado
//            } else if (action.equals(Literal.parseLiteral("bloquearSalvador3"))) { //robo inimigo - remover no futuro
//            	model.BloquearAction("sample.RoboFazNada (3)",_nomeRoboInimigo); //no modelo final nao sera tratado
//            } else if (action.equals(Literal.parseLiteral("bloquearSalvador4"))) { //robo inimigo - remover no futuro
//            	model.BloquearAction("sample.RoboFazNada (4)",_nomeRoboInimigo); //no modelo final nao sera tratado

        	//backup
//        	}else if (action.equals(Literal.parseLiteral("ludibriar"))){
//            	model.LudibriarInimigo();

//        		
//        	//---------------------------------------------------------------------
////        		if (_numeroRobos >= 3) {
////        			model.BloquearAgSave();
////        		}
//        		//System.out.println("#########AG inimigo bloquearAction ");
//        		
////            } else if (action.getFunctor().equals(move_towards)) {
////                //int x = (int)((NumberTerm)action.getTerm(0)).solve();
////                //int y = (int)((NumberTerm)action.getTerm(1)).solve();
////                model.moveTowards();
            } else {
            	//se nenhuma acao, significa que terminou os planos
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        updatePercepts();
        _esperar = true;
        //para nos humanos
        aguardar(200);
        return true;
    }
    
    /** creates the agents perception based on the ModelSalvarRefem */
    void updatePercepts() {
        clearPercepts();
        
        //Location ag_save = model.getAgPos(0);
        //Location ag_refem = model.getAgPos(1);

        atualizarDadosRobosViaTCP(); //recupera os dados dos robos via TCP
    }
    
    public void atualizarDadosRobosViaTCP(){
    	//recupera os dados dos robos via TCP
    	for (int i = 0; i < _numeroRobos; i++) { atualizaDadosViaTCP(i); }
        
    	//new ControlePercepcoes(this, _numeroRobos).RealizarControlePercepcoes();
    	
//        if (r1.getNumeroRound() > _numeroRound) {
//        	_numeroRound = r1.getNumeroRound();
//        	//remove a percepcao que ele esta perto do refem
//        	//seria a reinicilizacao do jason
//        	//removePercept(agSave,Literal.parseLiteral("pertorefem.")); //comentado por enquanto
//        	removePercept(agQuatroAcoes,Literal.parseLiteral("pertorefem."));
//        	removePercept(agRefem, Literal.parseLiteral("refemseguir."));
//        	//problema: ao reiniciar o robocode, o jason pode nao rechamar este metodo
//        	//System.out.println("#inicio jason");
//        }
        
    }
    
//	public void Continuar(DadosRobos dados) {
//		//System.out.println("### DEVO CONTINUAR");
//		_esperar = false;
//		if (dados!=null) atualizarDadosRobosViaTCP();
//	}

	private void iniciaServidorTCP() {
		if (!_podeDormir) return;
		if (_server == null) {
			_server = new TCPServer(_portaServidorTCPJason);
			_server.setIJason(this);
			_server.start();
		}
	}

	@SuppressWarnings("unused")
	private void killServidorTCP() {
		if (!_podeDormir) return;
		_server.parar(); // _server.stop();
		_server = null;
	}
    
    /**
     *analise de pacotes TCP 
     * */
	public void analisePacote(byte[] pacote, DataOutputStream out) throws IOException {
		if (!_podeDormir) return;
//		CriadorPacotes cp = new CriadorPacotes();
//		switch (AnalisePacotes.getTipo(pacote)) {
//			case avisarJasonToJason:
//				//System.out.println("RECEBI UM PACOTE - JASON");
//				//jason recebeu um pacote do tipo 'continue a sua execucao'
//				this.Continuar(AnalisePacotes.getDadosRobo(pacote)); //recebe os dados do robo
//				//System.out.println("JASON - RESPOSTA via OUT - Cliente ROBOT TCP");
//				out.write(cp.pacoteRespostaJason()); //envia uma resposta ao cliente robo
//				break;
////			case enviarDadosRobos:
////				RoboScaneado re = AnalisePacotes.getDadosRoboEscaneado(pacote);
////				System.out.println(re.toString());
////				break;
//		}
	}

	



}

/**
 * 0 - parar
 * 1 - reiniciar robo
 * 2 - atirar
 * 3 - virar esquerda
 * 4 - virar direita
 * 5 - andar
 **/